﻿<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图像特征点分析工具</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f7fa;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        header {
            text-align: center;
            margin-bottom: 30px;
        }
        
        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
        }
        
        .description {
            color: #7f8c8d;
            margin-bottom: 20px;
        }
        
        .main-content {
            display: grid;
            grid-template-columns: 1fr 2fr;
            gap: 20px;
        }
        
        .upload-section {
            background: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        
        .upload-area {
            border: 2px dashed #3498db;
            border-radius: 8px;
            padding: 30px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
            margin-bottom: 20px;
        }
        
        .upload-area:hover {
            background-color: #eaf2f8;
            border-color: #2980b9;
        }
        
        .upload-icon {
            font-size: 48px;
            color: #3498db;
            margin-bottom: 10px;
        }
        
        .file-input {
            display: none;
        }
        
        .file-list {
            margin-top: 20px;
        }
        
        .file-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 4px;
            margin-bottom: 8px;
        }
        
        .remove-btn {
            color: #e74c3c;
            cursor: pointer;
            font-weight: bold;
        }
        
        .settings {
            margin: 20px 0;
        }
        
        .form-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
        }
        
        select, input {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }
        
        .btn {
            background: #3498db;
            color: white;
            border: none;
            padding: 12px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            width: 100%;
            transition: background 0.3s;
        }
        
        .btn:hover {
            background: #2980b9;
        }
        
        .btn:disabled {
            background: #bdc3c7;
            cursor: not-allowed;
        }
        
        .results-section {
            background: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }
        
        .results-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
            gap: 20px;
            margin-top: 20px;
        }
        
        .result-card {
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            overflow: hidden;
        }
        
        .result-header {
            background: #f1f8ff;
            padding: 10px;
            font-weight: 600;
            border-bottom: 1px solid #e0e0e0;
        }
        
        .result-content {
            padding: 15px;
        }
        
        .image-container {
            text-align: center;
            margin-bottom: 15px;
        }
        
        .image-container img {
            max-width: 100%;
            max-height: 200px;
            border-radius: 4px;
        }
        
        .feature-canvas {
            width: 100%;
            height: 200px;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
        }
        
        .status {
            text-align: center;
            padding: 10px;
            margin: 10px 0;
            border-radius: 4px;
        }
        
        .status.info {
            background: #d1ecf1;
            color: #0c5460;
        }
        
        .status.error {
            background: #f8d7da;
            color: #721c24;
        }
        
        .status.success {
            background: #d4edda;
            color: #155724;
        }
        
        .loading {
            display: none;
            text-align: center;
            margin: 20px 0;
        }
        
        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
            margin: 0 auto;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .empty-state {
            text-align: center;
            padding: 40px;
            color: #7f8c8d;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>图像特征点分析工具</h1>
            <p class="description">上传图像并分析其特征点分布</p>
        </header>
        
        <div class="main-content">
            <div class="upload-section">
                <div class="upload-area" id="uploadArea">
                    <div class="upload-icon">📁</div>
                    <p>点击或拖放图像文件到这里</p>
                    <p class="small">支持 JPG, PNG, GIF 格式</p>
                </div>
                <input type="file" id="fileInput" class="file-input" multiple accept="image/*">
                
                <div class="file-list" id="fileList"></div>
                
                <div class="settings">
                    <div class="form-group">
                        <label for="auxFuncType">辅助函数类型:</label>
                        <select id="auxFuncType">
                            <option value="sin">正弦函数 (sin)</option>
                            <option value="radial">径向正弦波 (radial)</option>
                            <option value="linear" selected>线性梯度 (linear)</option>
                            <option value="gaussian">高斯分布 (gaussian)</option>
                            <option value="exponential">指数衰减 (exponential)</option>
                            <option value="random">随机噪声 (random)</option>
                        </select>
                    </div>
                    
                    <div class="form-group">
                        <label for="stride">采样步长:</label>
                        <input type="range" id="stride" min="1" max="20" value="5">
                        <span id="strideValue">5</span> 像素
                    </div>
                    
                    <div class="form-group">
                        <label for="iterations">迭代次数:</label>
                        <input type="range" id="iterations" min="1" max="10" value="3">
                        <span id="iterationsValue">3</span> 次
                    </div>
                </div>
                
                <button id="processBtn" class="btn">开始分析</button>
                
                <div class="loading" id="loading">
                    <div class="spinner"></div>
                    <p>处理中，请稍候...</p>
                </div>
                
                <div class="status info" id="statusInfo" style="display: none;"></div>
            </div>
            
            <div class="results-section">
                <h2>分析结果</h2>
                <div class="results-container" id="resultsContainer">
                    <div class="empty-state" id="emptyState">
                        <p>暂无分析结果</p>
                        <p>请先上传图像并点击"开始分析"</p>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        const uploadedFiles = [];
        const n = 256;
        const k = Math.PI;
        
        // DOM 元素
        const uploadArea = document.getElementById('uploadArea');
        const fileInput = document.getElementById('fileInput');
        const fileList = document.getElementById('fileList');
        const strideInput = document.getElementById('stride');
        const strideValue = document.getElementById('strideValue');
        const iterationsInput = document.getElementById('iterations');
        const iterationsValue = document.getElementById('iterationsValue');
        const processBtn = document.getElementById('processBtn');
        const loading = document.getElementById('loading');
        const statusInfo = document.getElementById('statusInfo');
        const resultsContainer = document.getElementById('resultsContainer');
        const emptyState = document.getElementById('emptyState');
        
        // 事件监听器
        uploadArea.addEventListener('click', () => fileInput.click());
        uploadArea.addEventListener('dragover', handleDragOver);
        uploadArea.addEventListener('drop', handleDrop);
        fileInput.addEventListener('change', handleFileSelect);
        strideInput.addEventListener('input', updateStrideValue);
        iterationsInput.addEventListener('input', updateIterationsValue);
        processBtn.addEventListener('click', processImages);
        
        // 更新步长显示
        function updateStrideValue() {
            strideValue.textContent = strideInput.value;
        }
        
        // 更新迭代次数显示
        function updateIterationsValue() {
            iterationsValue.textContent = iterationsInput.value;
        }
        
        // 处理拖放事件
        function handleDragOver(e) {
            e.preventDefault();
            e.stopPropagation();
            uploadArea.style.backgroundColor = '#eaf2f8';
        }
        
        function handleDrop(e) {
            e.preventDefault();
            e.stopPropagation();
            uploadArea.style.backgroundColor = '';
            
            const files = e.dataTransfer.files;
            handleFiles(files);
        }
        
        // 处理文件选择
        function handleFileSelect(e) {
            const files = e.target.files;
            handleFiles(files);
        }
        
        // 处理上传的文件
        function handleFiles(files) {
            if (files.length === 0) return;
            
            for (let i = 0; i < files.length; i++) {
                const file = files[i];
                
                // 检查文件类型
                if (!file.type.match('image.*')) {
                    showStatus('只能上传图像文件', 'error');
                    continue;
                }
                
                // 检查文件是否已存在
                if (uploadedFiles.some(f => f.name === file.name && f.size === file.size)) {
                    showStatus(`文件 ${file.name} 已经存在`, 'info');
                    continue;
                }
                
                // 添加到上传文件列表
                const fileObj = {
                    file: file,
                    fileName: file.name,
                    fileSize: formatFileSize(file.size),
                    dataUrl: URL.createObjectURL(file)
                };
                
                uploadedFiles.push(fileObj);
                
                // 添加到文件列表显示
                const fileItem = document.createElement('div');
                fileItem.className = 'file-item';
                fileItem.innerHTML = `
                    <span>${file.name} (${fileObj.fileSize})</span>
                    <span class="remove-btn" data-index="${uploadedFiles.length - 1}">×</span>
                `;
                
                fileList.appendChild(fileItem);
                
                // 添加移除事件
                fileItem.querySelector('.remove-btn').addEventListener('click', function() {
                    const index = parseInt(this.getAttribute('data-index'));
                    removeFile(index);
                });
            }
            
            // 清空文件输入
            fileInput.value = '';
            
            // 更新按钮状态
            updateProcessButton();
            
            showStatus(`成功添加 ${files.length} 个文件`, 'success');
        }
        
        // 移除文件
        function removeFile(index) {
            if (index >= 0 && index < uploadedFiles.length) {
                // 释放对象 URL
                URL.revokeObjectURL(uploadedFiles[index].dataUrl);
                
                uploadedFiles.splice(index, 1);
                
                // 重新渲染文件列表
                renderFileList();
                
                // 更新按钮状态
                updateProcessButton();
            }
        }
        
        // 渲染文件列表
        function renderFileList() {
            fileList.innerHTML = '';
            
            uploadedFiles.forEach((fileObj, index) => {
                const fileItem = document.createElement('div');
                fileItem.className = 'file-item';
                fileItem.innerHTML = `
                    <span>${fileObj.fileName} (${fileObj.fileSize})</span>
                    <span class="remove-btn" data-index="${index}">×</span>
                `;
                
                fileList.appendChild(fileItem);
                
                // 添加移除事件
                fileItem.querySelector('.remove-btn').addEventListener('click', function() {
                    const index = parseInt(this.getAttribute('data-index'));
                    removeFile(index);
                });
            });
        }
        
        // 更新处理按钮状态
        function updateProcessButton() {
            processBtn.disabled = uploadedFiles.length === 0;
        }
        
        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        // 显示状态信息
        function showStatus(message, type) {
            statusInfo.textContent = message;
            statusInfo.className = `status ${type}`;
            statusInfo.style.display = 'block';
            
            // 5秒后自动隐藏
            setTimeout(() => {
                statusInfo.style.display = 'none';
            }, 5000);
        }
        
        // 辅助函数矩阵生成
        function createAuxiliaryMatrix(funcType = 'sin', size = 256) {
            const matrix = new Array(size);
            for (let i = 0; i < size; i++) {
                matrix[i] = new Array(size);
                for (let j = 0; j < size; j++) {
                    const u = (i / size) * 2.55;
                    const v = (j / size) * 2.55;
                    
                    let value;
                    switch (funcType) {
                        case 'sin':
                            value = Math.sin(k * (u * u + v * v));
                            break;
                        case 'radial':
                            const radius = Math.sqrt(u * u + v * v);
                            value = Math.sin(k * radius);
                            break;
                        case 'linear':
                            value = (u + v) / 5.1;
                            break;
                        case 'gaussian':
                            const center = 1.275;
                            value = Math.exp(-((u - center) * (u - center) + (v - center) * (v - center)) / 0.5);
                            break;
                        case 'exponential':
                            value = Math.exp(-0.1 * (u * u + v * v));
                            break;
                        case 'random':
                            value = Math.random() * 2 - 1;
                            break;
                        default:
                            value = Math.sin(k * (u * u + v * v));
                    }
                    
                    // 将值映射到 [1, 256]
                    matrix[i][j] = Math.floor(((value + 1) / 2) * 255) + 1;
                }
            }
            return matrix;
        }
        
        // 迭代函数 - 根据辅助函数类型使用不同的迭代规则
        function applyIteration(z_val, img_val, iteration, maxIterations, funcType) {
            // 确保值在合理范围内
            z_val = Math.max(1, Math.min(256, z_val));
            img_val = Math.max(1, Math.min(256, img_val));
            
            const progress = iteration / maxIterations;
            
            switch (funcType) {
                case 'sin':
                    // 正弦函数迭代：使用正弦波混合
                    return [
                        img_val,
                        (z_val + img_val * Math.sin(k * progress)) % 256
                    ];
                    
                case 'radial':
                    // 径向函数迭代：基于距离的混合
                    const distance = Math.sqrt(z_val * z_val + img_val * img_val) / 362;
                    return [
                        img_val,
                        (z_val * (1 - progress) + img_val * progress * Math.sin(k * distance)) % 256
                    ];
                    
                case 'linear':
                    // 线性迭代：简单线性混合
                    return [
                        img_val,
                        (z_val * (1 - progress) + img_val * progress) % 256
                    ];
                    
                case 'gaussian':
                    // 高斯迭代：使用高斯分布影响
                    const gaussian = Math.exp(-(progress - 0.5) * (progress - 0.5) / 0.08);
                    return [
                        img_val,
                        (z_val * (1 - gaussian) + img_val * gaussian) % 256
                    ];
                    
                case 'exponential':
                    // 指数迭代：指数衰减混合
                    const exponential = Math.exp(-2 * progress);
                    return [
                        img_val,
                        (z_val * exponential + img_val * (1 - exponential)) % 256
                    ];
                    
                case 'random':
                    // 随机迭代：引入随机因素
                    const randomFactor = 0.8 + 0.4 * Math.random();
                    return [
                        img_val,
                        (z_val * (1 - progress) + img_val * progress * randomFactor) % 256
                    ];
                    
                default:
                    // 默认使用线性迭代
                    return [
                        img_val,
                        (z_val * (1 - progress) + img_val * progress) % 256
                    ];
            }
        }
        
        // 图像处理函数 - 已修改迭代过程
        function processImage(fileObj, auxiliaryMatrix, stride, numIterations, auxFuncType) {
            return new Promise((resolve, reject) => {
                const img = new Image();
                
                img.onload = function() {
                    const canvas = document.createElement('canvas');
                    canvas.width = n;
                    canvas.height = n;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0, n, n);
                    
                    const imageData = ctx.getImageData(0, 0, n, n);
                    const data = imageData.data;
                    
                    const featurePoints = [];
                    
                    // 处理每个像素点
                    for (let x = 0; x < n; x += stride) {
                        for (let y = 0; y < n; y += stride) {
                            const index = (y * n + x) * 4;
                            const grayValue = Math.round((data[index] + data[index + 1] + data[index + 2]) / 3);
                            const normalizedGrayValue = Math.floor((grayValue / 255) * 255) + 1;
                            
                            let z_val = auxiliaryMatrix[x][y];
                            let img_val = normalizedGrayValue;
                            
                            // 应用迭代过程
                            for (let p = 0; p < numIterations; p++) {
                                [z_val, img_val] = applyIteration(z_val, img_val, p, numIterations, auxFuncType);
                            }
                            
                            // 保存最终的特征点 (x坐标和迭代后的灰度值)
                            featurePoints.push([x, img_val]);
                        }
                    }
                    resolve({
                        fileName: fileObj.fileName,
                        originalImage: img,
                        featurePoints: featurePoints
                    });
                };
                
                img.onerror = reject;
                
                // 使用之前创建的 dataUrl
                img.src = fileObj.dataUrl;
            });
        }
        
        // 可视化结果
        function visualizeResults(results, auxiliaryType) {
            // 隐藏空状态
            emptyState.style.display = 'none';
            
            // 清空结果容器
            resultsContainer.innerHTML = '';
            
            results.forEach((result, index) => {
                if (!result) return;
                
                const resultCard = document.createElement('div');
                resultCard.className = 'result-card';
                
                resultCard.innerHTML = `
                    <div class="result-header">${result.fileName}</div>
                    <div class="result-content">
                        <div class="image-container">
                            <img src="${result.originalImage.src}" alt="${result.fileName}">
                        </div>
                        <canvas class="feature-canvas" id="feature-canvas-${index}"></canvas>
                    </div>
                `;
                
                resultsContainer.appendChild(resultCard);
                
                // 绘制特征点
                const canvas = document.getElementById(`feature-canvas-${index}`);
                const ctx = canvas.getContext('2d');
                canvas.width = 260;
                canvas.height = 260;
                
                // 清空画布
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                
                // 绘制背景网格
                ctx.strokeStyle = '#f0f0f0';
                ctx.lineWidth = 0.5;
                for (let i = 0; i <= 260; i += 20) {
                    ctx.beginPath();
                    ctx.moveTo(i, 0);
                    ctx.lineTo(i, 260);
                    ctx.stroke();
                    
                    ctx.beginPath();
                    ctx.moveTo(0, i);
                    ctx.lineTo(260, i);
                    ctx.stroke();
                }
                
                // 绘制特征点
                ctx.fillStyle = 'rgba(65, 105, 225, 0.7)';
                result.featurePoints.forEach(point => {
                    ctx.beginPath();
                    ctx.arc(point[0], point[1], 1, 0, Math.PI * 2);
                    ctx.fill();
                });
                
                // 添加坐标轴标签
                ctx.fillStyle = '#333';
                ctx.font = '12px Arial';
                ctx.fillText('X Position', 110, 250);
                ctx.save();
                ctx.translate(15, 130);
                ctx.rotate(-Math.PI / 2);
                ctx.fillText('Gray Value', 0, 0);
                ctx.restore();
                
                // 添加标题
                ctx.fillStyle = '#2c3e50';
                ctx.font = 'bold 10px Arial';
                ctx.fillText(`辅助函数: ${auxiliaryType}`, 10, 15);
            });
        }
        
        // 主处理函数
        async function processImages() {
            if (uploadedFiles.length === 0) {
                showStatus('请先上传图像文件', 'error');
                return;
            }
            
            // 显示加载状态
            loading.style.display = 'block';
            processBtn.disabled = true;
            
            const auxFuncType = document.getElementById('auxFuncType').value;
            const stride = parseInt(document.getElementById('stride').value);
            const numIterations = parseInt(document.getElementById('iterations').value);
            
            console.log(`使用辅助函数: ${auxFuncType}, 步长: ${stride}, 迭代次数: ${numIterations}`);
            
            const auxiliaryMatrix = createAuxiliaryMatrix(auxFuncType, n);
            const results = [];
            
            try {
                for (let i = 0; i < uploadedFiles.length; i++) {
                    const result = await processImage(uploadedFiles[i], auxiliaryMatrix, stride, numIterations, auxFuncType);
                    results.push(result);
                    console.log(`图像 ${i + 1} 处理完成`);
                }
                
                visualizeResults(results, auxFuncType);
                showStatus(`处理完成！共处理 ${uploadedFiles.length} 张图像`, 'success');
            } catch (error) {
                console.error('处理过程中出错:', error);
                showStatus(`处理过程中出错: ${error.message}`, 'error');
            } finally {
                // 隐藏加载状态
                loading.style.display = 'none';
                processBtn.disabled = false;
            }
        }
        
        // 初始化
        updateStrideValue();
        updateIterationsValue();
        updateProcessButton();
    </script>
</body>
</html>
